package com.itheima.tlias.aspect;

import com.itheima.tlias.annotation.AutoFill;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Aspect
@Component
public class AutoFillAspect {

    /**
     * 在执行方法前对注解进行操作
     * @param joinPoint
     * @param autoFill
     * @return
     * @throws Throwable
     */
    @Around ("@annotation(autoFill)")
    public Object around(ProceedingJoinPoint joinPoint, AutoFill autoFill) throws Throwable{
         Object[] args = joinPoint.getArgs();
        // 获取实体
        Object entity = args[0];
        // 判断被切入的方法是新增还是修改
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        if (method.getName().equals("insert")){
            Class<?> clazz = entity.getClass();
            clazz.getDeclaredMethod("setCreateTime", LocalDateTime.class).invoke(entity, LocalDateTime.now());
            clazz.getDeclaredMethod("setUpdateTime", LocalDateTime.class).invoke(entity, LocalDateTime.now());
             args[0] = entity;
            return joinPoint.proceed();
        }
        if (method.getName().equals("update")) {
            Class<?> clazz = entity.getClass();
            /**
             * 使用 Field 设置字段值
             * 直接访问字段：这种方式是通过反射直接访问类中的字段，并设置其值。
             * 绕过封装：它会绕过任何 setter 方法中的逻辑（如参数验证、业务逻辑等），直接修改字段的值。
             * 适用场景：当你需要直接修改字段值而不调用 setter 方法时使用
             */
            //Field setUpdateTime = clazz.getDeclaredField("updateTime");
            //setUpdateTime.setAccessible(true);
            //setUpdateTime.set(entity, LocalDateTime.now());
            /**
             * 使用 Method 调用 setter 方法
             * 调用 setter 方法：这种方式是通过反射调用类中的 setter 方法来设置字段值。
             * 保持封装：它会执行 setter 方法中的所有逻辑，包括但不限于参数验证、业务逻辑等。
             */
            clazz.getDeclaredMethod("setUpdateTime", LocalDateTime.class).invoke(entity,LocalDateTime.now());
            args[0] = entity;
            return joinPoint.proceed();
        }
        return joinPoint.proceed();
    }
}
